//
//  HKTableAdapter.m
//  KSBridgeDesign
//
//  Created by huzhaoke on 2021/4/17.
//

#import "HKTableAdapter.h"
#import "HKListCellModel.h"
#import "HKListSectionModel.h"
#import "HKSectionHeaderFooterViewModel.h"
#import "HKListAdapter+Private.h"
#import "HKListSectionModel+Private.h"
#import "HKListCellModel+Private.h"


static NSString *const hHKAdapterErrorCellIdentifier = @"hk_error_cell";


@interface HKTableAdapter () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, weak) UITableView *tableView;

@property (nonatomic, strong) dispatch_source_t reloadSource;

@end

@implementation HKTableAdapter

- (void)dealloc {
    _tableView.delegate = nil;
    _tableView.dataSource = nil;
    if (_reloadSource) {
        dispatch_source_cancel(_reloadSource);
        _reloadSource = nil;
    }
}

- (instancetype)initWithTableView:(UITableView *)tableView {
    if (self = [super initWithView:tableView]) {
        _tableView = tableView;
        _tableView.delegate = self;
        _tableView.dataSource = self;
        dispatch_queue_t reloadSourceQueue = dispatch_queue_create("com.kuaishou.tableadapter.reload.source", DISPATCH_QUEUE_SERIAL);
        _reloadSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, reloadSourceQueue);
        __weak typeof(self) weakSelf = self;
        dispatch_source_set_event_handler(_reloadSource, ^{
            [weakSelf _reloadTable];
        });
        dispatch_resume(_reloadSource);
    }
    return self;
}

+ (UITableView *)createTableViewWithFrame:(CGRect)rect {
    UITableView *tableView = [[UITableView alloc] initWithFrame:rect style:UITableViewStyleGrouped];
    tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    tableView.backgroundColor = [UIColor clearColor];
    if (@available(iOS 11.0, *)) {
        tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    }
    return tableView;
}

#pragma mark - TableViewDataSource
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self cellModelsCountAtSection:section];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self sectionCount];
}

- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    HKListSectionModel *sectionModel = [self sectionModelAtSection:indexPath.section];
    NSAssert(sectionModel, @"sectionItem不能为空");
    if (!sectionModel) { // Protection
        return [self errorCell];
    }
    __weak typeof(self) weakSelf = self;
    sectionModel.reloadBlock = ^(HKListSectionModel * _Nonnull sectionModel, NSIndexPath * _Nonnull indexPath, UITableViewRowAnimation animation, void (^ _Nullable completion)(BOOL)) {
        [weakSelf reloadSectionModel:sectionModel withRowAnimation:animation completion:completion];
    };
    HKListCellModel *cellModel = [self cellModelAtIndexPath:indexPath];
    NSAssert(cellModel, @"cellItem不能为空");
    if (!cellModel) { // Protection
        return [self errorCell];
    }
    [self registerCellModel:cellModel];
    cellModel.reloadBlock = ^(HKListCellModel *cellModel, NSIndexPath *indexPath, UITableViewRowAnimation animation, ReloadCompleteBlock completion) {
        [weakSelf reloadCellModel:cellModel withRowAnimation:animation completion:^(BOOL finished) {
            if (completion) {
                completion(finished);
            }
        }];
    };
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellModel.identifier forIndexPath:indexPath];
    NSAssert(cell, @"cell不能为空, 请检查identifier");
    if (!cell) { // Protection
        return [self errorCell];
    }
    cellModel.isReloading = YES;
    cellModel.sectionModel = sectionModel;
    cellModel.indexPath = indexPath;
    if (cellModel.render) {
        cellModel.render(cellModel, cell);
    }
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    HKListCellModel *cellModel = [self cellModelAtIndexPath:indexPath];
    CGFloat cellHeight = cellModel.cellHeight;
    return MAX(cellHeight, .0);
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    HKListCellModel *cellModel = [self cellModelAtIndexPath:indexPath];
    return cellModel.cellHeight;
}

#pragma mark - TableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    HKListCellModel *cellModel = [self cellModelAtIndexPath:indexPath];
    if (cellModel.cellDidSelectBlock) {
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        cellModel.cellDidSelectBlock(cellModel, indexPath.copy, cell);
    }
}

- (void)tableView:(UITableView *)tableView
  willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
    HKListCellModel *cellModel = [self cellModelAtIndexPath:indexPath];
    cellModel.isReloading = NO;
    if (cellModel.cellWillDisplayBlock) {
        cellModel.cellWillDisplayBlock(cellModel, indexPath.copy, cell);
    }
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSIndexPath *calculateIndexPath = [tableView indexPathForCell:cell];
    if ([indexPath isEqual:calculateIndexPath]) {
        HKListCellModel *cellModel = [self cellModelAtIndexPath:indexPath];
        if (cellModel.cellDidEndDisplayBlock) {
            cellModel.cellDidEndDisplayBlock(cellModel, indexPath.copy, cell);
        }
        
        if (cellModel.cellDetachBlock) {
            cellModel.cellDetachBlock(cellModel, indexPath.copy, cell);
        }
    } else {
        // 如果是删除cell的话，cell的indexPath计算出来是nil
    }
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    HKListSectionModel *sectionModel = [self sectionModelAtSection:section];
    return sectionModel.headerViewModel.title;
}

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    HKListSectionModel *sectionModel = [self sectionModelAtSection:section];
    return sectionModel.footerViewModel.title;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    HKListSectionModel *sectionModel = [self sectionModelAtSection:section];
    return [self viewforHeaderFooterModel:sectionModel.headerViewModel tableView:tableView];
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    HKListSectionModel *sectionModel = [self sectionModelAtSection:section];
    return [self viewforHeaderFooterModel:sectionModel.footerViewModel tableView:tableView];
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    HKListSectionModel *sectionModel = [self sectionModelAtSection:section];
    if (!sectionModel) {
        return self.tableView.style == UITableViewStylePlain ? 0.01 : CGFLOAT_MIN;
    }
    return sectionModel.headerViewModel.height > 0 ? sectionModel.headerViewModel.height : 0.01;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    HKListSectionModel *sectionModel = [self sectionModelAtSection:section];
    if (!sectionModel) {
        return self.tableView.style == UITableViewStylePlain ? .01 : CGFLOAT_MIN;
    }
    return sectionModel.footerViewModel.height > 0 ? sectionModel.footerViewModel.height > 0 : 0.01;
}

#pragma mark - tableDatasource


#pragma mark - tableViewPrivateView
- (UIView *)viewforHeaderFooterModel:(HKSectionHeaderFooterViewModel *)headerAndFooterModel
                           tableView:(UITableView *)tableView {
    if (!headerAndFooterModel) {
        return nil;
    }
    
    [self registerHeaderFooterModel:headerAndFooterModel];
    
    UITableViewHeaderFooterView *view = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:headerAndFooterModel.identifier];
    
    if (!view) {
        return nil;
    }
    if (headerAndFooterModel.render) {
        headerAndFooterModel.render(headerAndFooterModel, view);
    }
    
    return view;
}

- (void)registerCellModel:(HKListCellModel *)cellModel {
    NSString *identifier = cellModel.identifier;
    if (!identifier || [self containsCellIdentifier:identifier]) {
        return;
    }
    [self.tableView registerClass:cellModel.cellClass forCellReuseIdentifier:identifier];
    [self addCellIdentifier:identifier];
}

- (void)registerHeaderFooterModel:(HKSectionHeaderFooterViewModel *)headerAndFooterModel {
    NSString *identifier = headerAndFooterModel.identifier;
    if (!identifier || [self containsCellIdentifier:identifier]) {
        return;
    }
    if (!headerAndFooterModel.viewClass) {
        return;
    }
    [self.tableView registerClass:headerAndFooterModel.viewClass forHeaderFooterViewReuseIdentifier:identifier];
    [self addHeaderFooterIdentifier:identifier];
}

- (nonnull UITableViewCell *)errorCell {
    return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                  reuseIdentifier:hHKAdapterErrorCellIdentifier];
}

- (void)reloadCellModel:(HKListCellModel *)cellModel
      withRowAnimation:(UITableViewRowAnimation)animation
            completion:(void (^ _Nullable)(BOOL finished))completion {
    NSIndexPath *indexPath = cellModel.indexPath;
    if (!indexPath) {
        completion ? completion(NO) : nil;
        return;
    }
    
    if (![self.tableView.indexPathsForVisibleRows containsObject:indexPath]) {
        completion ? completion(NO) : nil;
        return;
    }
    
    [self performBatchUpdates:^(HKTableAdapter *adapter) {
        if ([self.tableView.indexPathsForVisibleRows containsObject:indexPath]) {
            [adapter.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:animation];
        }
    } animated:animation != UITableViewRowAnimationNone completion:completion];
}

- (void)reloadSectionModel:(HKListSectionModel *)sectionModel
         withRowAnimation:(UITableViewRowAnimation)animation
               completion:(void (^ _Nullable)(BOOL finished))completion{
    NSUInteger section = [self.sectionModels indexOfObject:sectionModel];
    if (section == NSNotFound) {
        completion ? completion(NO) : nil;
        return;
    }
    
    [self performBatchUpdates:^(HKTableAdapter *adapter) {
        if (section >= 0 && section < adapter.tableView.numberOfSections) {
            [adapter.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
        } else {
            NSAssert(NO, @"KwaiTableAdapter reload invalid section");
        }
    } animated:animation != UITableViewRowAnimationNone completion:completion];
}

#pragma mark - Animation Batch Updates
- (void)performBatchUpdates:(void (NS_NOESCAPE ^)(HKTableAdapter *adapter))updates
                   animated:(BOOL)animated
                 completion:(void (^ _Nullable)(BOOL finished))completion {
    [self _updateTableWithUpdate:updates animated:animated completion:completion];
}

- (void)_updateTableWithUpdate:(void (NS_NOESCAPE ^)(HKTableAdapter *adapter))update
                      animated:(BOOL)animated
                    completion:(void (^ _Nullable)(BOOL finished))completion {
    [CATransaction begin];
    [CATransaction setCompletionBlock:^{
        if (completion) {
            completion(YES);
        }
    }];
    [CATransaction setDisableActions:!animated];
    [CATransaction setAnimationDuration:animated ? 0.2 : 0];

    [self.tableView beginUpdates];
    
    if (update) {
        update(self);
    }
    
    [self.tableView endUpdates];
    
    if (@available(iOS 11, *)) {
        // iOS 11 及以上 会执行 scrollViewDidScroll 不修改系统默认行为
    } else {
        // iOS 11 以下 TableView 不会执行 scrollViewDidScroll
        [self scrollViewDidScroll:self.tableView];
    }
    
    [CATransaction commit];
}

#pragma mark -publicMethod
- (void)reloadData {
    dispatch_source_merge_data(_reloadSource, 1);
}

- (void)_reloadTable {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.tableView reloadData];
    });
}

@end
